home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Overload Trio 2
/
Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO
/
dir24
/
jnos110g.zip
/
CONVERS.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-04-17
|
51KB
|
1,772 lines
/* convers server - based on conversd written by DK5SG
* ported to WNOS by DB3FL - 9109xx/9110xx
* ported to NOS by PE1NMB - 920120
* Mods by PA0GRI
* Cleanup, and additional mods by WG7J
*/
#include <time.h>
#include <ctype.h>
#ifdef UNIX
#include <sys/types.h>
#include <sys/stat.h>
#endif
#ifdef MSDOS
#include <io.h>
#endif
#include "global.h"
#ifdef CONVERS
#include "mailbox.h"
#include "netuser.h"
#include "pktdrvr.h"
#include "timer.h"
#include "cmdparse.h"
#include "usock.h"
#include "socket.h"
#include "session.h"
#include "files.h"
#include "mailutil.h"
#define LINK 1
#define space
char Chostname[CNAMELEN+1];
void conv_incom __ARGS((int s,void *t,void *p));
int ConvUsers;
int ConvHosts;
#ifdef space
static char cnumber[] = "*** Channel numbers must be in the range 0..%d.\n";
#else
static char cnumber[] = "* range 0..%d.\n";
#endif
#define MAXCHANNEL 32767
#define LINELEN 256
/* The convers daemon stack needs to be AT LEAST 4 times the LINELEN !
* since quiet a few of the commands have local vars of size 2 or 3 * LINELEN.
*/
#define CDAEMONSTACK 2048
#define CLINKSTACK 1024
#define MAX_WAITTIME (60*60*3)
#define NAMELEN 10
struct convection {
int type; /* Connection type */
#define CT_UNKNOWN 0
#define CT_USER 1
#define CT_HOST 2
#define CT_CLOSED 3
char name[NAMELEN+1]; /* Name of user or host */
char host[NAMELEN+1]; /* Name of host where user is logged on */
struct convection *via; /* Pointer to neighbor host */
char *data; /* room for some personal data */
int channel; /* Channel number */
int32 time; /* Connect time */
int maxq; /* Maximum outstanding data before link reset */
int locked; /* Set if mesg already sent */
int fd; /* Socket descriptor */
int flags; /* User flags */
#define CLOSE_SOCK 1
#define USE_SOUND 2
/* This buffer is only needed for local users; a lot of space is wasted
* for users from other hosts (256 bytes per user!). Fixed 930728 - WG7J
* char ibuf[LINELEN];
*/
char *ibuf; /* Input buffer */
unsigned long received; /* Number of bytes received */
unsigned long xmitted; /* Number of bytes transmitted */
struct convection *next; /* Linked list pointer */
};
#define CM_UNKNOWN (1 << CT_UNKNOWN)
#define CM_USER (1 << CT_USER)
#define CM_HOST (1 << CT_HOST)
#define CM_CLOSED (1 << CT_CLOSED)
#define NULLCONNECTION ((struct convection *) 0)
static struct convection *convections;
struct permlink {
char name[NAMELEN+1]; /* Name of link */
int32 addr; /* address to link to */
struct convection *convection; /* Pointer to associated connection */
int32 statetime; /* Time of last (dis)connect */
int tries; /* Number of connect tries */
int32 waittime; /* Time between connect tries */
int32 retrytime; /* Time of next connect try */
int fd; /* socket descriptor */
struct permlink *next; /* Linked list pointer */
};
#define NULLPERMLINK ((struct permlink *) 0)
struct filter_link {
struct filter_link *next;
int32 addr;
};
#define NULLFL ((struct filter_link *) 0)
#ifdef LINK
struct proc *Linker;
static void connect_permlinks __ARGS((int a,void *b,void *c));
static void update_permlinks __ARGS((char *name,struct convection *cp));
#endif
static void free_connection __ARGS((register struct convection *cp));
static char *formatline __ARGS((char *prefix,char *text));
static char *timestring __ARGS((long gmt));
static void clear_locks __ARGS((void));
static void send_msg_to_user __ARGS((char *fromname,char *toname,char *text));
static void send_user_change_msg __ARGS((char *name,char *host,int oldchannel,int newchannel));
static void send_invite_msg __ARGS((char *fromname,char *toname,int channel));
static void personal_command __ARGS((struct convection *cp));
static void bye_command __ARGS((struct convection *cp));
static void send_msg_to_channel __ARGS((char *fromname,int channel,char *text));
static void free_closed_connections __ARGS((void));
static struct convection *alloc_connection __ARGS((int fd));
static void process_commands __ARGS((struct convection *cp,struct mbx *m));
static void set_personal __ARGS((struct convection *cp));
static void version_command __ARGS((struct convection *cp));
static struct filter_link *Filterlinks;
static int FilterMode;
static struct permlink *permlinks;
extern char Ccall[AXALEN];
static int docfilter __ARGS((int argc,char *argv[],void *p));
static int dochostname __ARGS((int argc,char *argv[],void *p));
static int dociface __ARGS((int argc,char *argv[],void *p));
static int doconfcall __ARGS((int argc,char *argv[],void *p));
static int doclink __ARGS((int argc,char *argv[],void *p));
static int doct4 __ARGS((int argc,char *argv[],void *p));
static int docmaxwait __ARGS((int argc,char *argv[],void *p));
static int dohmaxq __ARGS((int argc,char *argv[],void *p));
static int doumaxq __ARGS((int argc,char *argv[],void *p));
static int dotdisc __ARGS((int argc,char *argv[],void *p));
static int docdefaultchannel __ARGS((int argc,char *argv[],void *p));
static struct cmds DFAR Ccmds[] = {
"channel", docdefaultchannel, 0, 0, NULLCHAR,
"filter", docfilter, 0, 0, NULLCHAR,
"hmaxq", dohmaxq, 0, 0, NULLCHAR,
"hostname", dochostname,0, 0, NULLCHAR,
"interface",dociface, 0, 0, NULLCHAR,
"maxwait", docmaxwait, 0, 0, NULLCHAR,
#ifdef AX25
"mycall", doconfcall, 0, 0, NULLCHAR,
#endif
#ifdef LINK
"link", doclink, 0, 0, NULLCHAR,
#endif
#ifdef AX25
"t4", doct4, 0, 0, NULLCHAR,
#endif
"tdisc", dotdisc, 0, 0, NULLCHAR,
"umaxq", doumaxq, 0, 0, NULLCHAR,
NULLCHAR,
};
/* Multiplexer for top-level convers command */
int
doconvers(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return subcmd(Ccmds,argc,argv,p);
}
#ifdef AX25
/* Display or change our AX.25 conference call */
static int
doconfcall(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char tmp[AXBUF];
if(argc < 2){
tprintf("%s\n",pax25(tmp,Ccall));
return 0;
}
if(setcall(Ccall,argv[1]) == -1)
return -1;
return 0;
}
int32 CT4init = 7200; /* 2 hours default */
/* Set link redundancy timer */
static int
doct4(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setlong(&CT4init,"Conf. redundancy timer (sec)",argc,argv);
}
#endif /* AX25 */
int32 CMaxwait = MAX_WAITTIME;
/* Set maxwait time for timed out links */
static int
docmaxwait(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setlong(&CMaxwait,"Re-link max wait (sec)",argc,argv);
}
int HMaxQ = 5*1024;
/* Set max qlimit for host links */
static int
dohmaxq(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setint(&HMaxQ,"Max. Host Queue (bytes)",argc,argv);
}
int UMaxQ = 1024;
/* Set max qlimit for user links */
static int
doumaxq(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setint(&UMaxQ,"Max. User Queue (bytes)",argc,argv);
}
int CDefaultChannel;
/* Set the initial channel */
static int
docdefaultchannel(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setint(&CDefaultChannel,"Default channel",argc,argv);
}
int32 Ctdiscinit;
/* Set convers redundancy timer */
static int
dotdisc(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setlong(&Ctdiscinit,"redundancy timer (sec)",argc,argv);
}
static int
dochostname(argc,argv,p)
int argc;
char *argv[];
void *p;
{
if(argc == 1)
tprintf("%s\n",Chostname);
else {
strncpy(Chostname,argv[1],NAMELEN);
Chostname[CNAMELEN] = '\0';
}
return 0;
}
static int
docfilter(argc,argv,p)
int argc;
char *argv[];
void *p;
{
int32 addr;
struct filter_link *fl;
if(argc == 1) {
if(Filterlinks) {
tprintf("Mode is: %s\n", FilterMode ? "Accept" : "Refuse");
for(fl=Filterlinks;fl;fl=fl->next)
tprintf("%s\n",inet_ntoa(fl->addr));
}
return 0;
}
if(!stricmp(argv[1],"mode")) {
if(argc == 2)
tprintf("Mode is: %s\n", FilterMode ? "Accept" : "Refuse");
else {
if(*argv[2] == 'a' || *argv[2] == 'A')
FilterMode = 1;
else
FilterMode = 0;
}
return 0;
}
if((addr = resolve(argv[1])) == 0) {
tprintf(Badhost,argv[1]);
return 1;
}
/* check to see if we already have this in the list */
for(fl=Filterlinks;fl;fl=fl->next)
if(fl->addr == addr)
return 0; /* already have this one ! */
/* Seems like a new one */
fl = (struct filter_link *)callocw(1,sizeof(struct filter_link));
fl->addr = addr;
fl->next = Filterlinks;
Filterlinks = fl;
return 0;
}
#ifdef LINK
static int
doclink(argc,argv,p)
int argc;
char *argv[];
void *p;
{
int32 addr;
struct permlink *pl;
if(argc == 1) {
for(pl=permlinks;pl;pl=pl->next)
tprintf("%10s: %s\n",pl->name,inet_ntoa(pl->addr));
return 0;
}
if((addr = resolve(argv[1])) == 0) {
tprintf(Badhost,argv[1]);
return 1;
}
/* check to see if we already have a link to such animal,
* this happens when we stop and restart the server - WG7J
*/
for(pl=permlinks;pl;pl=pl->next)
if(pl->addr == addr)
return 1; /* already have this one ! */
/* Seems like a new link ! Go add it */
pl = (struct permlink *)callocw(1,sizeof(struct permlink ));
pl->addr = addr;
pl->next = permlinks;
permlinks = pl;
if(argc > 2) {
strncpy(pl->name,argv[2],NAMELEN);
update_permlinks(pl->name,NULLCONNECTION);
} else
strcpy(pl->name,"Unknown");
if(!Linker)
Linker = newproc("Clinker",CLINKSTACK,connect_permlinks,0,0,NULL,0);
return 0;
}
#endif
static int
dociface(argc,argv,p)
int argc;
char *argv[];
void *p;
{
return setflag(argc,argv[1],IS_CONV_IFACE,argv[2]);
}
/* Stop convers server */
int
conv0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
int16 port;
if(argc < 2)
port = IPPORT_CONVERS;
else
port = atoi(argv[1]);
#ifdef LINK
if(Linker)
killproc(Linker);
#endif
return stop_tcp(port);
}
/* Start up convers server */
int
conv1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
int16 port;
if(argc < 2)
port = IPPORT_CONVERS;
else
port = atoi(argv[1]);
return start_tcp(port,"CONVERS Server",conv_incom,CDAEMONSTACK);
}
static void
free_connection(cp)
register struct convection *cp;
{
register struct permlink *p;
for(p = permlinks; p; p = p->next)
if(p->convection == cp)
p->convection = NULLCONNECTION;
/* if(cp->data) free() checks for NULL; smaller code :-) */
free(cp->data);
free(cp->ibuf);
if(cp->flags & CLOSE_SOCK)
close_s(cp->fd);
free((char *) cp);
}
static void
free_closed_connections()
{
register struct convection *cp,*p;
time_t currtime;
currtime = time(&currtime);
for(p = NULLCONNECTION,cp = convections; cp; )
if(cp->type == CT_CLOSED ||
cp->type == CT_UNKNOWN && cp->time + 300 < currtime) {
if(p) {
p->next = cp->next;
free_connection(cp);
cp = p->next;
} else {
convections = cp->next;
free_connection(cp);
cp = convections;
}
} else {
p = cp;
cp = cp->next;
}
}
static void
update_permlinks(name,cp)
char *name;
struct convection *cp;
{
register struct permlink *p;
time_t currtime;
for(p = permlinks; p; p = p->next)
if(!strcmp(p->name,name)) {
currtime = time(&currtime);
p->convection = cp;
p->statetime = currtime;
p->tries = 0;
p->waittime = 60;
p->retrytime = currtime + p->waittime;
}
}
static struct convection *
alloc_connection(fd)
int fd;
{
register struct convection *cp;
time_t currtime;
currtime = time(NULL);
cp = (struct convection *)callocw(1,sizeof(struct convection ));
cp->ibuf = (char *)callocw(1,LINELEN);
cp->fd = fd;
cp->maxq = UMaxQ; /* Maximum qlimit for user */
cp->flags = CLOSE_SOCK+USE_SOUND; /* close on exit, by default */
cp->time = currtime;
cp->next = convections;
convections = cp;
return cp;
}
#ifdef LINK
/* check the host links for backlogged data.
* If larger then set threshold, kill the link.
* WG7J, 930208
*/
void check_buffer_overload(void) {
struct convection *p;
/* check the size of the outstanding data buffers */
for(p = convections; p; p = p->next)
if((p->maxq != 0) && (socklen(p->fd,1) > p->maxq)) {
/* notify everyone of this special :-) occasion */
bye_command(p);
/* Blow this one out of the water */
shutdown(p->fd,2);
close_s(p->fd);
}
}
void
connect_permlinks(a,b,c)
int a;
void *b;
void *c;
{
int s;
register struct permlink *p;
struct sockaddr_in cport;
time_t currtime;
for(;;) {
pause(15000L);
for(p = permlinks; p; p = p->next) {
currtime = time(&currtime);
if(p->convection || p->retrytime > currtime)
continue;
p->tries++;
p->waittime <<= 1;
if(p->waittime > CMaxwait)
p->waittime = CMaxwait;
p->retrytime = p->waittime + currtime;
cport.sin_family = AF_INET;
cport.sin_port = IPPORT_CONVERS;
cport.sin_addr.s_addr = p->addr; /* we've resolved this earlier */
if((s = socket(AF_INET,SOCK_STREAM,0)) == -1)
continue;
if(connect(s,(char *)&cport,SOCKSIZE) == -1) {
shutdown(s,2); /* to make sure it doesn't linger around */
close_s(s); /* WG7J - 9207228 */
continue;
}
p->fd = s;
if(newproc("permlink",CDAEMONSTACK,conv_incom,s,(void *)TELNET,NULL,0) == NULLPROC){
shutdown(s,2); /* blow it out of the water :-) */
close_s(s);
}
}
/* This is now called from the garbage collect process, such that it
* checks even when the linker process isn't running! - WG7J
check_buffer_overload();
*/
}
}
#endif
static void
clear_locks()
{
register struct convection *p;
for(p = convections;p;p = p->next)
p->locked = 0;
}
static void send_sounds(struct convection *p) {
if(p->flags & USE_SOUND)
p->xmitted += usputs(p->fd,"");
}
static void
send_user_change_msg(name,host,oldchannel,newchannel)
char *name,*host;
int oldchannel,newchannel;
{
register struct convection *p;
for(p = convections; p; p = p->next) {
if(p->type == CT_USER && !p->via && !p->locked) {
if(p->channel == oldchannel) {
if(newchannel >= 0) {
p->xmitted += usprintf(p->fd,
"*** %s switched to channel %d.\n",
name,newchannel);
} else
p->xmitted += usprintf(p->fd,
"*** %s signed off.\n",name);
p->locked = 1;
}
if(p->channel == newchannel) {
send_sounds(p);
p->xmitted += usprintf(p->fd,
"*** %s signed on.\n",name);
p->locked = 1;
}
}
if(p->type == CT_HOST && !p->locked) {
p->xmitted += usprintf(p->fd,
"/\377\200USER %s %s %d %d %d\n",
name,host,0,oldchannel,newchannel);
p->locked = 1;
}
}
return;
}
#ifdef Oldcode
static char *
formatline(prefix,text)
char *prefix,*text;
{
#define PREFIXLEN 10
#define CONVLINELEN 79
register char *f,*t,*x;
register int l,lw;
static char buf[2*LINELEN];
for(f = prefix,t = buf; *f; *t++ = *f++) ;
l = (int)(t - buf);
f = text;
for(;;) {
while(isspace(uchar(*f)))
f++;
if(!*f) {
*t++ = '\n';
*t = '\0';
return buf;
}
for(x = f; *x && !isspace(uchar(*x)); x++) ;
lw = (int)(x - f);
if(l > PREFIXLEN && l + 1 + lw > CONVLINELEN) {
*t++ = '\n';
l = 0;
}
do {
*t++ = ' ';
l++;
} while(l < PREFIXLEN);
while(lw--) {
*t++ = *f++;
l++;
}
}
}
static void
send_msg_to_user(fromname,toname,text)
char *fromname,*toname,*text;
{
register struct convection *p;
char buffer[2*LINELEN];
for(p = convections; p; p = p->next) {
if(p->type == CT_USER && !strcmp(p->name,toname))
if(p->via) {
if(!p->via->locked) {
p->via->xmitted += usprintf(p->via->fd,
"/\377\200UMSG %s %s %s\n",fromname,toname,text);
p->via->locked = 1;
}
} else {
if(!p->locked) {
if(strcmp(fromname,"conversd")) {
sprintf(buffer,"<*%s*>:",fromname);
p->xmitted += usputs(p->fd,formatline(buffer,text));
} else {
p->xmitted += usputs(p->fd,text);
p->xmitted += usputc('\n');
}
p->locked = 1;
}
}
}
return;
}
static void
send_msg_to_channel(fromname,channel,text)
char *fromname;
int channel;
char *text;
{
char buffer[3*LINELEN];
register struct convection *p;
for(p = convections; p; p = p->next) {
if(p->type == CT_USER && p->channel == channel)
if(p->via) {
if(!p->via->locked) {
p->via->xmitted += usprintf(p->via->fd,
"/\377\200CMSG %s %d %s\n",fromname,channel,text);
p->via->locked = 1;
}
} else {
if(!p->locked) {
sprintf(buffer,"<%s>:",fromname);
p->xmitted += usputs(p->fd,formatline(&buffer[0],text));
p->locked = 1;
}
}
}
}
#else /* Oldcode */
/* Returns a formatted version of the given text.
* The prefix appears at the beginning of the text, and each
* line after the first will be indented to column PREFIXLEN.
* All whitespace (SPACE and TAB) will be replaced by a single
* SPACE character,
* Lines will be filled to be CONVLINELEN characters long, wrapping
* words as necessary.
*
* This uses an static internal buffer rather than one passed by
* the caller. This increases the static memory used by the program
* even if converse isn't active. If we passed a buffer allocated
* on the stack, the process's stack would have to be large enough.
*/
static char *
formatline(prefix, text)
char *prefix, *text;
{
# define PREFIXLEN 10
# define CONVLINELEN 79
# define FMTBUFLEN LINELEN
static char buf[FMTBUFLEN];
register char *f, *t;
register int l, lw;
register int left = FMTBUFLEN-2;
/* Runs of characters in delims[] will be collapsed into a single
space by formatline().
*/
char *delims = " \t\n\r";
/*
# define BPUTC(c) if (left > 0) { left--; *t++ = (c); } else
*/
# define BPUTC(c) do{ if (left > 0) { left--; *t++ = (c); }} while(0)
/* Copy prefix into buf; set l to length of prefix.
*/
l = 0;
for (f = prefix, t = buf; *f; ) {
BPUTC (*f++);
l++;
}
f = text;
for (;;) {
/* Skip leading spaces */
while (isspace(uchar(*f)))
f++;
/* Return if nothing more or no room left */
if (!*f || (left <= 0)) {
*t++ = '\n'; /* don't use BPUTC; do even if !left */
*t = '\0';
return buf;
}
/* Find length of next word (seq. of non-blanks) */
lw = strcspn (f, delims);
/* If the word would extend past end of line, do newline */
if (l > PREFIXLEN && (l + 1 + lw) > CONVLINELEN) {
BPUTC ('\n');
l = 0;
}
/* Put out a single space */
do {
BPUTC (' ');
l++;
} while(l < PREFIXLEN);
/* Put out the word */
while(lw--) {
BPUTC (*f++);
l++;
}
}
# undef BPUTC
}
static void
send_msg_to_user(fromname,toname,text)
char *fromname,*toname,*text;
{
register struct convection *p;
for (p = convections; p; p = p->next) {
if(p->type == CT_USER && !strcmp(p->name,toname))
if(p->via) {
if(!p->via->locked) {
p->via->xmitted += usprintf(p->via->fd,
"/\377\200UMSG %.10s %.10s %.220s\n",
fromname, toname, text);
p->via->locked = 1;
}
} else {
if(!p->locked) {
if(strcmp(fromname,"conversd")) {
char prefix[NAMELEN+10];
char *buf;
sprintf(prefix,"<*%.10s*>:",fromname);
buf = formatline (prefix,text);
p->xmitted += strlen (buf);
(void) usputs (p->fd, buf);
} else { /* not from conversd */
p->xmitted += strlen (text);
(void) usputs (p->fd, text);
} /* not from conversd */
p->locked = 1;
} /* if not locked */
} /* not via */
} /* for */
}
static void
send_msg_to_channel(fromname,channel,text)
char *fromname;
int channel;
char *text;
{
register struct convection *p;
for (p = convections; p; p = p->next) {
if(p->type == CT_USER && p->channel == channel)
if(p->via) {
if(!p->via->locked) {
p->via->xmitted += usprintf(p->via->fd,
"/\377\200CMSG %.10s %d %.220s\n",
fromname, channel, text);
p->via->locked = 1;
}
} else {
if(!p->locked) {
char prefix[NAMELEN+10];
char *buf;
sprintf(prefix,"<%.10s>:", fromname);
buf = formatline (prefix, text);
p->xmitted += strlen (buf);
(void) usputs (p->fd, buf);
p->locked = 1;
} /* not locked */
} /* not via */
} /* for */
}
#endif /* Oldcode */
extern char *Months[]; /* in smtpserv.c */
static char *
timestring(gmt)
long gmt;
{
static char buffer[10];
struct tm *tm;
time_t currtime;
time(&currtime);
tm = localtime(&gmt);
if(gmt + 24 * 60 * 60 > currtime)
sprintf(buffer," %2d:%02d",tm->tm_hour,tm->tm_min);
else
sprintf(buffer,"%-3.3s %2d",Months[tm->tm_mon],tm->tm_mday);
return buffer;
}
#ifdef space
char invitetext[] = "\n*** Message from %s at%s ...\nPlease join convers channel %d.\n\n";
char mbinvitetext[] = "\n*** Message from %s at%s ...\nPlease join convers by typing 'CONV %d' !\n\n";
char responsetext[] = "*** Invitation sent to %s @ %s";
#else
char invitetext[] = "\n*** Msg frm %s at%s ...\nPse join ch. %d.\n\n";
char mbinvitetext[] = "\n*** Msg frm %s at%s ...\nPse hit 'CONV %d' to join convers.\n\n";
char responsetext[] = "*** sent to %s @ %s";
#endif
char cnvd[] = "conversd";
static void
send_invite_msg(fromname,toname,channel)
char *fromname,*toname;
int channel;
{
char buffer[LINELEN];
struct convection *p;
#ifdef MAILBOX
struct mbx *m;
#endif
time_t currtime;
currtime = time(&currtime);
#ifdef MAILBOX
/* Check users in the mailbox that aren't active */
for(m=Mbox;m;m=m->next){
if(m->state == MBX_CMD && !stricmp(m->name,toname)) {
usprintf(m->user,mbinvitetext,fromname,timestring(currtime),channel);
usflush(m->user);
clear_locks();
sprintf(buffer,responsetext,toname,"BBS@");
strcat(buffer,Hostname);
send_msg_to_user(cnvd,fromname,buffer);
return;
}
}
#endif
/* check the current convers users */
for(p = convections; p; p = p->next) {
if(p->type == CT_USER && !stricmp(p->name,toname)) {
if(p->channel == channel) {
clear_locks();
sprintf(buffer,"*** User %s is already on this channel.\n",toname);
send_msg_to_user(cnvd,fromname,buffer);
return;
}
if(!p->via && !p->locked) {
p->xmitted += usprintf(p->fd,invitetext,fromname, \
timestring(currtime),channel);
clear_locks();
sprintf(buffer,responsetext,toname,Chostname);
send_msg_to_user(cnvd,fromname,buffer);
return;
}
if(p->via && !p->via->locked) {
p->via->xmitted += usprintf(p->via->fd,
"/\377\200INVI %s %s %d\n",fromname,toname,channel);
return;
}
}
}
/* Nothing found locally, invite user on all links */
for(p = convections; p; p = p->next) {
if(p->type == CT_HOST && !p->locked) {
p->xmitted += usprintf(p->fd,
"/\377\200INVI %s %s %d\n",fromname,toname,channel);
}
}
return;
}
static void
bye_command(cp)
struct convection *cp;
{
struct convection *p;
switch(cp->type) {
case CT_UNKNOWN:
cp->type = CT_CLOSED;
break;
case CT_USER:
cp->type = CT_CLOSED;
clear_locks();
send_user_change_msg(cp->name,cp->host,cp->channel,-1);
ConvUsers--;
break;
case CT_HOST:
cp->type = CT_CLOSED;
update_permlinks(cp->name,NULLCONNECTION);
for(p = convections; p; p = p->next)
if(p->via == cp) {
p->type = CT_CLOSED;
clear_locks();
send_user_change_msg(p->name,p->host,p->channel,-1);
}
ConvHosts--;
break;
case CT_CLOSED:
break;
}
}
static void
channel_command(cp)
struct convection *cp;
{
char s[7];
int newchannel;
s[0] = '\0';
sscanf(cp->ibuf,"%*s %6s",s);
if(s[0] == '\0') {
#ifdef space
cp->xmitted += usprintf(cp->fd,"*** You are on channel %d.\n",cp->channel);
#else
cp->xmitted += usprintf(cp->fd,"* On channel %d.\n",cp->channel);
#endif
return;
}
newchannel = atoi(s);
if(newchannel < 0) {
/* || newchannel > MAXCHANNEL) { */
cp->xmitted += usprintf(cp->fd,cnumber,MAXCHANNEL);
return;
}
if(newchannel == cp->channel) {
cp->xmitted += usprintf(cp->fd,
"*** Already on channel %d.\n",cp->channel);
return;
}
send_user_change_msg(cp->name,cp->host,cp->channel,newchannel);
cp->channel = newchannel;
cp->xmitted += usprintf(cp->fd,"*** Now on channel %d.\n",cp->channel);
return;
}
static void
help_command(cp)
struct convection *cp;
{
#ifdef space
cp->xmitted += usputs(cp->fd,"Commands may be abbreviated. Commands are:\n");
cp->xmitted += usputs(cp->fd,"/? or /HELP Print help information\n");
cp->xmitted += usputs(cp->fd,"/BYE Terminate the convers session\n");
cp->xmitted += usputs(cp->fd,"/CHANNEL Switch to channel n\n");
cp->xmitted += usputs(cp->fd,"/EXIT Terminate the convers session\n");
cp->xmitted += usputs(cp->fd,"/INV user Invite user to join your channel\n");
cp->xmitted += usputs(cp->fd,"/LINKS [L] List all connections to other hosts\n");
cp->xmitted += usputs(cp->fd,"/MSG user text Send a private message to user\n");
cp->xmitted += usputs(cp->fd,"/PERSONAL text Set or show some personal text\n");
cp->xmitted += usputs(cp->fd,"/QUIT Terminate the convers session\n");
cp->xmitted += usputs(cp->fd,"/SOUNDS y|n Turn bell on or off\n");
cp->xmitted += usputs(cp->fd,"/WHO [QUICK] List all users and their channel numbers\n");
cp->xmitted += usputs(cp->fd,"/WR user text Send a private message to user\n"
"***\n");
#else
cp->xmitted += usputs(cp->fd,"no help\n");
#endif
return;
}
static void
version_command(cp)
struct convection *cp;
{
usprintf(cp->fd,"%s Convers Server.\n",shortversion);
}
static void
invite_command(cp)
struct convection *cp;
{
char toname[NAMELEN+1];
char *cp1;
toname[0] = '\0';
sscanf(cp->ibuf,"%*s %10s",toname);
if((cp1=strchr(toname,'@')) != NULLCHAR)
*cp1 = '\0';
if(toname[0] != '\0')
send_invite_msg(cp->name,toname,cp->channel);
return;
}
int ShowConfLinks(int s, int full) {
int num;
struct convection *pc;
struct permlink *pp;
char tmp[20];
num = usprintf(s,"Host State Since%s\n",
(full) ? " NextTry Tries Receivd Xmitted" : "");
for(pc = convections; pc; pc = pc->next)
if(pc->type == CT_HOST)
num += usprintf(s,
(full) ?
"%-10s Connected %s %7lu %7lu\n" :
"%-10s Connected %s\n",
pc->name,
timestring(pc->time),
pc->received,
pc->xmitted);
for(pp = permlinks; pp; pp = pp->next)
if(!pp->convection || pp->convection->type != CT_HOST) {
strcpy(tmp,timestring(pp->retrytime));
num += usprintf(s,
(full) ?
"%-10s %-12s %s %s %5d\n" :
"%-10s %-12s %s\n",
pp->name,
pp->convection ? "Connecting" : "Disconnected",
timestring(pp->statetime),
tmp,
pp->tries);
}
num += usputs(s,"***\n");
return num;
}
static void
links_command(cp)
struct convection *cp;
{
char full[3];
int f = 0;
full[0] = '\0';
sscanf(cp->ibuf,"%*s %2s",full);
if(*full == 'l' || *full == 'L')
f = 1;
cp->xmitted += ShowConfLinks(cp->fd,f);
return;
}
static void
msg_command(cp)
struct convection *cp;
{
char dummy[LINELEN],toname[NAMELEN+1],*text;
register struct convection *p;
toname[0] = '\0';
sscanf(cp->ibuf,"%s %10s",dummy,toname);
text = &cp->ibuf[0];
text += strlen(dummy) + strlen(toname) + 2;
if(!*text)
return;
for(p = convections; p; p = p->next)
if(p->type == CT_USER && !strcmp(p->name,toname))
break;
if(!p)
cp->xmitted += usprintf(cp->fd,"*** No such user: %s.\n",toname);
else
send_msg_to_user(cp->name,toname,text);
return;
}
/* Set some personal data, like name and qth - WG7J */
#define PERSONAL_LEN 31
static void
personal_command(cp)
struct convection *cp;
{
struct convection *p;
char *cp2;
if((cp2 = strchr(cp->ibuf,' ')) != NULLCHAR) {
cp2++;
if(*cp2) { /* there actually is an argument */
if(cp->data)
free(cp->data);
rip(cp->ibuf); /* get rid of ending '\n' */
if(strlen(cp2) > PERSONAL_LEN)
*(cp2+PERSONAL_LEN) = '\0';
cp->data = strdup(cp2);
/* update all links too ! - WG7J */
for(p=convections;p;p=p->next)
if(p->type == CT_HOST)
p->xmitted += usprintf(p->fd,"/\377\200UDAT %s %s %s\n",
cp->name,cp->host,cp->data);
return;
}
}
cp->xmitted += usprintf(cp->fd,"*** data set to: %s\n", \
cp->data ? cp->data : "" );
return;
}
/* find the personal information for this user */
void set_personal(struct convection *cp) {
FILE *fp;
char *cp1;
if((fp = fopen(Cinfo,"r")) == NULL)
return;
while(fgets(cp->ibuf,LINELEN,fp) != NULL) {
cp1 = cp->ibuf;
/* find end of name */
while(*cp1 != ' ' && *cp1 != '\t' && *cp1 != '\0')
cp1++;
if(!*cp1)
continue;
*cp1 = '\0';
if(stricmp(cp->name,cp->ibuf))
continue;
/* Found personal data ! */
*cp1 = ' ';
fclose(fp);
personal_command(cp);
return;
}
fclose(fp);
}
void SendMotd(int s) {
FILE *fp;
if((fp=fopen(ConvMotd,"r")) != NULL) {
sendfile(fp, s, ASCII_TYPE, 0, NULL);
fclose(fp);
}
}
/* protected by ftpusers file - WG7J */
static void
name_command(cp)
struct convection *cp;
{
int newchannel = CDefaultChannel;
char dummy[7];
char *path;
int pwdignore;
long privs;
cp->name[0] = '\0';
dummy[0] = '\0';
sscanf(cp->ibuf,"%*s %10s %6s",cp->name,dummy);
if(dummy[0] != '\0')
newchannel = atoi(dummy);
if(cp->name[0] == '\0')
return;
/* now check with ftpusers file - WG7J */
if((path = mallocw(MBXLINE)) == NULLCHAR)
return;
pwdignore = 1;
privs = userlogin(cp->name,NULLCHAR,&path,MBXLINE,&pwdignore);
free(path);
if(privs & NO_CONVERS)
return;
strlwr(cp->name);
strcpy(cp->host,Chostname);
cp->type = CT_USER;
cp->xmitted += usprintf(cp->fd,
"Conference @ %s Type /HELP for help.\n",Chostname);
/* Send the motd */
SendMotd(cp->fd);
if(dummy[0] != '\0' && newchannel < 0) {
/* || newchannel > MAXCHANNEL) { */
cp->xmitted += usprintf(cp->fd,cnumber,MAXCHANNEL);
} else
cp->channel = newchannel;
send_user_change_msg(cp->name,cp->host,-1,cp->channel);
set_personal(cp);
ConvUsers++;
return;
}
/* Set or show the status of the 'sound' flag - WG7J */
static void
sounds_command(cp)
struct convection *cp;
{
char *cp2;
if((cp2 = strchr(cp->ibuf,' ')) != NULLCHAR) {
cp2++;
if(*cp2) { /* There is an argument */
if(*cp2 == 'n' || *cp2 == 'N') { /* Turn it off */
cp->flags &= ~USE_SOUND;
} else
cp->flags |= USE_SOUND;
return;
}
}
if(cp->flags & USE_SOUND)
usputs(cp->fd,"*** Sounds on\n");
else
usputs(cp->fd,"*** Sounds off\n");
return;
}
/* Print a user display, return the number of characters sent */
int ShowConfUsers(int s,int quick,char *name) {
int num,channel;
struct convection *p;
#ifdef MAILBOX
struct mbx *m;
#endif
char buffer[LINELEN];
if(quick) {
num = usputs(s,"Channel Users\n");
clear_locks();
do {
channel = -1;
for(p = convections; p; p = p->next) {
if(p->type == CT_USER &&
!p->locked &&
(channel < 0 || channel == p->channel)) {
if(channel < 0) {
channel = p->channel;
sprintf(buffer,"%7d",channel);
}
strcat(buffer," ");
strcat(buffer,p->name);
p->locked = 1;
}
}
if(channel >= 0) {
num += usputs(s,buffer);
num += usputc(s,'\n');
}
} while(channel >= 0);
} else {
num = usputs(s,"User Host Via Channel Time Personal\n");
for(p = convections; p; p = p->next) {
if(p->type == CT_USER) {
if(name == NULL || *name == '\0' || !stricmp(p->name,name))
num += usprintf(s,"%-10s %-10s %-10s %7d %s %s\n",
p->name,
p->host,
p->via ? p->via->name : "",
p->channel,
timestring(p->time),
p->data ? p->data : "" );
}
}
}
#ifdef MAILBOX
for(m=Mbox;m;m=m->next) {
if(m->state == MBX_CMD) {
if(quick)
num += usprintf(s," BBS %s\n",m->name);
else
num += usprintf(s,"%-10s BBS@%s\n",m->name,Hostname);
}
}
#endif
num += usputs(s,"***\n");
return num;
}
static void
who_command(cp)
struct convection *cp;
{
char buffer[LINELEN];
int quick = 0;
buffer[0] = '\0';
sscanf(cp->ibuf,"%*s %s",buffer);
if(buffer[0] == 'q')
quick = 1;
cp->xmitted += ShowConfUsers(cp->fd,quick,buffer);
return;
}
static void
h_cmsg_command(cp)
struct convection *cp;
{
char *text;
int channel;
char name[LINELEN],dummy[40];
sscanf(cp->ibuf,"%s %10s %d",dummy,name,&channel);
text = &cp->ibuf[0];
text += strlen(dummy) + strlen(name) + 2;
while(isspace(*text) == 0)
text++;
text++;
if(isprint(*text) != 0)
send_msg_to_channel(name,channel,text);
return;
}
/* Return 1 if the host is to be allowed, or 0 if refused - WG7J */
int Allow_host(int s) {
struct filter_link *fl;
struct sockaddr_in fsocket;
int i = sizeof(struct sockaddr_in);
if(Filterlinks) { /* Check for this ip address */
getpeername(s,(char *)&fsocket,&i);
for(fl=Filterlinks;fl;fl=fl->next)
if(fl->addr == fsocket.sin_addr.s_addr)
return FilterMode;
/* Not found ! */
return !FilterMode;
}
return 1;
}
static void
h_host_command(cp)
struct convection *cp;
{
char name[NAMELEN+1];
struct convection *p;
struct permlink *pp;
if(!Allow_host(cp->fd)) {
bye_command(cp);
return;
}
name[0] = '\0';
sscanf(cp->ibuf,"%*s %10s",name);
if(name[0] == '\0') {
bye_command(cp);
return;
}
for(p = convections; p; p = p->next)
if(!strcmp(p->name,name)) {
bye_command(p);
return;
}
for(pp = permlinks; pp; pp = pp->next)
if(!strcmp(pp->name,name) && pp->convection && pp->convection != cp) {
bye_command((strcmp(Chostname,name) < 0) ? pp->convection : cp);
return;
}
/*
if(cp->type != CT_UNKNOWN)
return;
*/
cp->type = CT_HOST;
cp->maxq = HMaxQ;
strcpy(cp->name,name); /* already allocated */
update_permlinks(name,cp);
cp->xmitted += usprintf(cp->fd,"/\377\200HOST %s\n",Chostname);
for(p = convections; p; p = p->next)
if(p->type == CT_USER) {
cp->xmitted += usprintf(cp->fd,
"/\377\200USER %s %s %d %d %d\n",
p->name,p->host,0,-1,p->channel);
if(p->data)
cp->xmitted += usprintf(cp->fd,
"/\377\200UDAT %s %s %s\n",
p->name,p->host,p->data);
}
ConvHosts++;
return;
}
static void
h_invi_command(cp)
struct convection *cp;
{
char fromname[NAMELEN+1],toname[NAMELEN+1];
int channel;
sscanf(cp->ibuf,"%*s %10s %10s %d",fromname,toname,&channel);
send_invite_msg(fromname,toname,channel);
return;
}
static void
h_loop_command(cp)
struct convection *cp;
{
char host[NAMELEN+1];
sscanf(cp->ibuf,"%*s %10s",host);
log(cp->fd, "conversd rx: LOOP %s",host);
bye_command(cp);
}
/* Command to take user's personal data across a link - WG7J */
void
h_udat_command(cp)
struct convection *cp;
{
char *name,*host,*data;
int len;
struct convection *p;
/* do a validity check first */
if((name = strchr(cp->ibuf,' ')) == NULLCHAR)
return;
name++;
if((host = strchr(name,' ')) == NULLCHAR)
return;
*host++ = '\0';
if((data = strchr(host,' ')) == NULLCHAR)
return;
*data++ = '\0';
rip(data); /* rip the '\n' */
if((len=strlen(data)) == 0)
return;
if(len > PERSONAL_LEN)
*(data+PERSONAL_LEN) = '\0';
/* everything seems fine, now find user ! */
for(p=convections;p;p=p->next) {
if(!strcmp(p->name,name) && !strcmp(p->host,host)) {
if(p->data)
free(p->data);
p->data = strdup(data);
}
/* update over other links Apr 12/93 VE3DTE */
if(p->type == CT_HOST && !p->locked)
p->xmitted += usprintf(p->fd,"/\377\200UDAT %s %s %s\n",
name,host,data);
}
}
static void
h_umsg_command(cp)
struct convection *cp;
{
char dummy[NAMELEN+1],fromname[NAMELEN+1],toname[NAMELEN+1],*text;
sscanf(cp->ibuf,"%s %10s %10s",dummy,fromname,toname);
text = &cp->ibuf[0];
text += strlen(dummy) + strlen(fromname) + strlen(toname) + 3;
if(*text)
send_msg_to_user(fromname,toname,text);
return;
}
static void
h_user_command(cp)
struct convection *cp;
{
char host[3*NAMELEN+1],name[3*NAMELEN+1];
int newchannel,oldchannel;
struct convection *p;
time_t currtime;
currtime = time(&currtime);
sscanf(cp->ibuf,"%*s %s %s %*s %d %d",name,host,&oldchannel,&newchannel);
/* Make sure the fields are not longer */
host[NAMELEN] = name[NAMELEN] = '\0';
for(p = convections; p; p = p->next)
if(p->type == CT_USER) {
/* new 920705 dl9sau */
/* If Neighbour2 registers a user on HostX, while someone has already
* been registered for HostX via Neighbour1, then we definitely have
* a loop ! We send a loop detect message and then close the link:
* /..LOOP <Chostname> <myneighbour> <host>
*
* The LOOP PREVENTION CODE detects ONLY a loop if it starts at this
* host. That's, why I suggest this code to be implemented in every
* conversd implementation.
*/
if (oldchannel < 0 && p->via != cp && !stricmp(p->host, host)) {
usprintf(cp->fd,"/\377\200LOOP %s %s %s\n", \
Chostname, host,p->via ? p->via->name : Chostname);
log(cp->fd, "conversd sent: LOOP %s",host);
bye_command(cp);
return;
}
if(p->channel == oldchannel && p->via == cp && \
!strcmp(p->name,name) && !strcmp(p->host,host))
break;
}
if(!p) {
p = (struct convection *)callocw(1,sizeof(struct convection ));
p->type = CT_USER;
strcpy(p->name,name);
strcpy(p->host,host);
p->via = cp;
p->channel = oldchannel;
p->time = currtime;
p->next = convections;
convections = p;
}
if((p->channel = newchannel) < 0) {
p->type = CT_CLOSED;
free_closed_connections(); /* VE3DTE Apr 5/93 */
}
send_user_change_msg(name,host,oldchannel,newchannel);
return;
}
struct cmdtable {
char *name;
void (*fnc)(struct convection *);
int states;
};
struct cmdtable DFAR cmdtable[] = {
"?", help_command, CM_USER,
"bye", bye_command, CM_USER,
"channel", channel_command, CM_USER,
"exit", bye_command, CM_USER,
"help", help_command, CM_USER,
"invite", invite_command, CM_USER,
"links", links_command, CM_USER,
"msg", msg_command, CM_USER,
"name", name_command, CM_UNKNOWN,
"personal", personal_command, CM_USER,
"quit", bye_command, CM_USER,
"sounds", sounds_command, CM_USER,
"version", version_command, CM_USER,
"who", who_command, CM_USER,
"write", msg_command, CM_USER,
"\377\200cmsg", h_cmsg_command, CM_HOST,
"\377\200host", h_host_command, CM_UNKNOWN,
"\377\200invi", h_invi_command, CM_HOST,
"\377\200loop", h_loop_command, CM_HOST,
"\377\200udat", h_udat_command, CM_HOST,
"\377\200umsg", h_umsg_command, CM_HOST,
"\377\200user", h_user_command, CM_HOST,
0, 0, 0,
};
static void
process_commands(cp,m)
struct convection *cp;
struct mbx *m;
{
char arg[LINELEN];
int arglen,size;
char *ccp;
struct cmdtable *cmdp;
for(;;) {
loop:
if(cp->type == CT_CLOSED)
break;
setflush(cp->fd,'\n');
usflush(cp->fd);
memset(cp->ibuf,0,LINELEN);
if(cp->type != CT_HOST)
alarm(Ctdiscinit * 1000L);
if((size = recvline(cp->fd,cp->ibuf,LINELEN-1)) <= 0)
break;
alarm(0L);
cp->received += size;
clear_locks();
cp->locked = 1;
if(*cp->ibuf == '/') {
ccp = &cp->ibuf[1];
arg[0] = '\0';
sscanf(ccp,"%s",arg);
arglen = strlen(arg);
/* We are about to parse a command; most likely there
* is alot of output; try to avoid fragmenting that
* data by doing our own flushing ! - WG7J
*/
setflush(cp->fd,-1);
for(cmdp = cmdtable; cmdp->name; cmdp++) {
if(!strncmpi(cmdp->name,arg,arglen)) {
if(cmdp->states & (1 << cp->type))
(*cmdp->fnc)(cp);
goto loop;
}
}
if(cp->type == CT_USER)
cp->xmitted += usprintf(cp->fd,
"*** Unknown command '/%s'. Type /HELP for help.\n",arg);
goto loop;
}
if((ccp = strpbrk(cp->ibuf,"\r\n")) != NULLCHAR)
*ccp = '\0';
if(isprint(cp->ibuf[0]) != 0 && cp->type == CT_USER)
send_msg_to_channel(cp->name,cp->channel,cp->ibuf);
}
bye_command(cp);
sockblock(cp->fd,SOCK_BLOCK);
free_closed_connections();
}
/* Incoming convers session */
void
conv_incom(s,t,p)
int s;
void *t;
void *p;
{
struct convection *cp;
struct permlink *pl;
sockowner(s,Curproc); /* We own it now */
sockmode(s,SOCK_BINARY);
sockblock(s,SOCK_NOTXBLOCK); /* prevent backlogs ! */
cp = alloc_connection(s);
cp->channel = CDefaultChannel;
for(pl = permlinks; pl; pl = pl->next)
if(pl->fd == s) {
pl->convection = cp;
cp->xmitted += usprintf(s,"/\377\200HOST %s\n",Chostname);
}
if(pl == NULLPERMLINK) {
#ifdef AX25
if((int)t == AX25TNC) { /* figure out call from socket */
struct usock *up;
char *chrp;
if((up = itop(s)) == NULLUSOCK) {
free_connection(cp);
return;
}
sockmode(s,SOCK_ASCII);
pax25(cp->name,up->cb.ax25->remote);
if((chrp=strchr(cp->name,'-')) != NULL)
*chrp = '\0';
strlwr(cp->name);
strcpy(cp->host,Chostname);
cp->type = CT_USER;
cp->xmitted += usprintf(s,
"Conference @ %s Type /HELP for help.\n",Chostname);
SendMotd(s);
clear_locks();
cp->locked = 1; /* send to everyone but ourself */
send_user_change_msg(cp->name,cp->host,-1,CDefaultChannel);
set_personal(cp);
ConvUsers++;
} else
#endif
usputs(cp->fd,"\npse login with '/n <call>'\n\n");
}
process_commands(cp,NULLMBX);
}
#ifdef MAILBOX
/* this is for Mailbox users */
void
mbox_converse(struct mbx *m,int channel)
{
struct convection *cp;
int oldflush;
sockblock(m->user,SOCK_NOTXBLOCK); /* prevent backlogs ! */
oldflush = setflush(m->user,'\n'); /* automatic line flushing */
cp = alloc_connection(m->user);
cp->channel = channel;
strcpy(cp->name,m->name);
strcpy(cp->host,Chostname);
cp->type = CT_USER;
cp->flags &= ~CLOSE_SOCK; /* do not close socket on exit */
cp->xmitted += usprintf(m->user,
"Conference @ %s Type /HELP for help.\n",Chostname);
SendMotd(m->user);
clear_locks();
cp->locked = 1; /* send to everyone but ourself */
send_user_change_msg(cp->name,cp->host,-1,cp->channel);
set_personal(cp);
ConvUsers++;
process_commands(cp,m);
setflush(m->user,oldflush);
}
#endif /* MAILBOX */
#endif /* CONVERS */